/**
 * Copyright (C) 2011 Ron Coleman. Contact: ronncoleman@gmail.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package bj.actor

import scala.actors.Actor
import bj.hkeeping.Ok
import scala.collection.mutable.HashMap
import bj.hkeeping.NotOk
import scala.actors.OutputChannel
import bj.util._
import Actor._
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._

case class Bet(player: Int, amt: Double)
case class GameStart(players: List[OutputChannel[Any]])
case class GameOver(payouts: HashMap[Int, Outcome])
case class Arrive(mailbox: OutputChannel[Any], player: Int, betAmt: Double)
case class TableInfo(tid: Int, minBet: Double, maxBet: Double)
case class Go
case class Stop
case class AddHumanPlayer(playerName: String, bankRoll: Double, betAmt: Double)
case class ShuffleSeed(seed: Int)
case class LoggedIn(pid: Int)
case class StartGame(pid: Int, betAmt: Double, seed: Int)
case class NewBet(bet: Double)
case class ActionTable(tid: Int)
case class ActionBot(action: String, tid: Int)

/**
 * @author Vikram
 *
 */

/** This object represents the house as a container of tables. */
object House extends Actor with Logs {

  val PORT = 9000
  RemoteActor.classLoader = getClass.getClassLoader

  /** Mailbox for the dealer */
  var dealer: OutputChannel[Any] = null
  var tableId = -1
  var nextId = 0
  var players = List[Player](new BetterPlayer("RonBot100", 10000, 100), new BetterPlayer("RonBot25", 5000, 25), new BetterPlayer("RonBot5", 1000, 5))
  var tables = List[Table](new Table(100, 200), new Table(25, 99), new Table(5, 24))
  var guiPlayer: OutputChannel[Any] = null
  override def toString = "house(" + nextId + ")"

  /** Adding a Player to the list of players with the details provided by GUI player */
  def addPlayer(actor: OutputChannel[Any], playerName: String, bankroll: Double, betAmt: Double): HumanPlayer = {
    var humanPlayer = new HumanPlayer(actor, playerName, bankroll, betAmt)
    players ++= List[Player](humanPlayer)
    humanPlayer
  }

  /** This method receives messages. */
  def act {

    Player.start(players)

    /** Declare the port I'm listening on */
    alive(PORT)

    /** Register me */
    register('bjHouse, self)

    debug(">>> started " + 'bjHouse + " @ " + PORT)

    loop {
      react {
        /** Receives a bet from a player and matches it to a table */
        case Bet(pid: Int, bet: Double) =>
          debug("house: received bet amt = " + bet)

          tables.find(t => t.bets.size < Table.MAX_PLAYERS && t.minBet <= bet) match {
            case None =>
              sender ! NotOk

            case Some(table) =>
              debug("house: sending table id = " + table.tid + " sender = " + sender)

              table ! Arrive(sender, pid, bet)
              sender ! TableInfo(table.tid, table.minBet, table.maxBet)
          }

        /** Receives a message to tell the tables to go */
        case Go =>
          debug("house: receive Go for " + tables.size + " tables")

          tables.foreach(t => t ! Go)

        /** Receives a message to tell the tables to stop */
        case Stop =>
          debug("house: received Stop")
          tables.foreach(t => t ! Stop)
          Player.start(players)

        /**
         *  Receives a message for Adding HumanPlayer with the details provided by the
         *  player at GUI and start the Human player and also sends back the sender
         *  with player id and assign sender as Gui player
         */
        case AddHumanPlayer(playerName: String, bankroll: Double, betAmt: Double) =>
          debug("adding human player to house")
          var human = addPlayer(sender, playerName, bankroll, betAmt)
          human.start
          sender ! LoggedIn(human.pid)
          guiPlayer = sender

        /**
         *  Receives the message from Gui to start the game with the details entered by
         *  the human player and search for the table with the bet amount and send that
         *  player go
         */
        case StartGame(pid: Int, betAmt: Double, seed: Int) =>

          tables.foreach(t => t ! ShuffleSeed(seed))

          players.find(p => p.pid == pid) match {

            case Some(player) =>
              player.betAmt = betAmt
              players.foreach(p => p ! Go)

            case None =>
              sender ! NotOk
          }

        /** Receives the table no from Table to know which table Human is playing */
        case ActionTable(tid) =>
          this.tableId = tid
          debug("ActionTable id =" + tid)

        /**
         *  Receives the message from Better Player that he's got Blackjack and
         *  forward it to HumanGUI through human player to display in GUI
         */
        case BotPlayerBlackJack(tableNo) =>
          if (tableId == tableNo) {
            guiPlayer ! BetterPlayerBlackJack
            debug("BotPlayer got BlackJack and tid =" + tableNo)
          }

        /**
         *  Receives the message from Better Player that his hand broke and
         *  forward it to HumanGUI through human player to display in GUI
         */
        case BotPlayerBroke(tableNo) =>
          if (tableId == tableNo) {
            guiPlayer ! BetterPlayerBroke
            debug("BotPlayer Broke and tid =" + tableNo)
          }

        /**
         *  Receives the Better Player with his result and forward it to
         *  HumanGUI through human player to display in GUI
         */
        case BotResult(tableNo, betAmount, bank) =>
          if (tableId == tableNo) {
            guiPlayer ! BotPlayerDetail(betAmount, bank)
            debug("BotPlayer bet =" + betAmount + " and bankroll =" + bank + " and the tableId is :" + tableNo)
          }

        /**
         *  Receives the Better Player's Strategy and forward it to
         *  HumanGUI through human player to display in GUI
         */
        case ActionBot(action, tableNo) =>
          if (tableId == tableNo) {
            guiPlayer ! ActionBot(action, tableNo)
            debug("BotPlayer action =" + action + " and the tableId is :" + tableNo)
          }
        /** Receives something completely from left field */
        case dontKnow =>
          /** Got something we REALLY didn't expect */
          debug(this + " got unexpected" + dontKnow)
      }
    }
  }
}